Add a `cap-std-apis` feature with open/create
authorColin Walters <walters@verbum.org>
Wed, 2 Feb 2022 22:47:52 +0000 (17:47 -0500)
committerColin Walters <walters@verbum.org>
Fri, 6 May 2022 16:53:57 +0000 (12:53 -0400)
I'm trying to make more use of `cap-std` in our stack, and
this will be a key enabling API.

Actually a notable side benefit of this is that we don't need
to teach the ostree C code itself to use `openat2`, we inherit
cap-std's setup.

All of the internal ostree code using the prior `openat()` should
continue to work.

I only did basic sanity checking of this; there may be bugs
in other APIs.

rust-bindings/rust/Cargo.toml
rust-bindings/rust/src/lib.rs
rust-bindings/rust/src/repo.rs
rust-bindings/rust/tests/repo/mod.rs
rust-bindings/rust/tests/util/mod.rs

index 5f2a444565a85c3d2a75403a3b9fa6fb8b8fc8b1..61f929a54a9cfa6cabc0d3a091f220ac41a56315 100644 (file)
@@ -29,6 +29,8 @@ members = [".", "sys"]
 
 [dependencies]
 bitflags = "1.2.1"
+cap-std = { version = "0.24", optional = true}
+io-lifetimes = { version = "0.5", optional = true}
 ffi = { package = "ostree-sys", path = "sys", version = "0.9.1" }
 gio = "0.14"
 glib = "0.14.4"
@@ -42,8 +44,10 @@ thiserror = "1.0.20"
 maplit = "1.0.2"
 openat = "0.1.19"
 tempfile = "3"
+cap-tempfile = "0.24"
 
 [features]
+cap-std-apis = ["cap-std", "io-lifetimes", "v2017_10"]
 dox = ["ffi/dox"]
 v2014_9 = ["ffi/v2014_9"]
 v2015_7 = ["v2014_9", "ffi/v2015_7"]
index ae2debb523f1ac8027b23649465950dfe3c6b1c5..a1ba65e092f9d4f9399cb1cebc7cc6e2d052abe8 100644 (file)
@@ -12,6 +12,8 @@
 // Re-export our dependencies.  See https://gtk-rs.org/blog/2021/06/22/new-release.html
 // "Dependencies are re-exported".  Users will need e.g. `gio::File`, so this avoids
 // them needing to update matching versions.
+#[cfg(feature = "cap-std-apis")]
+pub use cap_std;
 pub use ffi;
 pub use gio;
 pub use glib;
index 688956927c1d5511f2bea9300c352c792ef99491..ec38ce8325d655de27ab6b3760e0fbff4b4433a8 100644 (file)
@@ -1,5 +1,7 @@
 #[cfg(any(feature = "v2016_4", feature = "dox"))]
 use crate::RepoListRefsExtFlags;
+#[cfg(feature = "cap-std-apis")]
+use crate::RepoMode;
 use crate::{Checksum, ObjectDetails, ObjectName, ObjectType, Repo, RepoTransactionStats};
 use ffi::OstreeRepoListObjectsFlags;
 use glib::ffi as glib_sys;
@@ -97,6 +99,26 @@ impl Repo {
         Repo::new(&gio::File::for_path(path.as_ref()))
     }
 
+    #[cfg(feature = "cap-std-apis")]
+    /// A version of [`open_at`] which uses cap-std.
+    pub fn open_at_dir(dir: &cap_std::fs::Dir, path: &str) -> Result<Repo, glib::Error> {
+        use std::os::unix::io::AsRawFd;
+        crate::Repo::open_at(dir.as_raw_fd(), path, gio::NONE_CANCELLABLE)
+    }
+
+    #[cfg(feature = "cap-std-apis")]
+    /// A version of [`create_at`] which uses cap-std, and also returns the opened repo.
+    pub fn create_at_dir(
+        dir: &cap_std::fs::Dir,
+        path: &str,
+        mode: RepoMode,
+        options: Option<&glib::Variant>,
+    ) -> Result<Repo, glib::Error> {
+        use std::os::unix::io::AsRawFd;
+        crate::Repo::create_at(dir.as_raw_fd(), path, mode, options, gio::NONE_CANCELLABLE)?;
+        Repo::open_at_dir(dir, path)
+    }
+
     /// A wrapper for [`prepare_transaction`] which ensures the transaction will be aborted when the guard goes out of scope.
     pub fn auto_transaction<P: IsA<gio::Cancellable>>(
         &self,
index 007a0d83383da8ffb48e13945ca0890dfcb53174..2cfde3db723adc9ecb4aac4c497b80233f972ce9 100644 (file)
@@ -26,6 +26,26 @@ fn should_commit_content_to_repo_and_list_refs_again() {
     assert_eq!(checksum, refs["test"]);
 }
 
+#[test]
+#[cfg(feature = "cap-std-apis")]
+fn cap_std_commit() {
+    let test_repo = CapTestRepo::new();
+
+    assert!(test_repo.repo.require_rev("nosuchrev").is_err());
+
+    let mtree = create_mtree(&test_repo.repo);
+    let checksum = commit(&test_repo.repo, &mtree, "test");
+
+    assert_eq!(test_repo.repo.require_rev("test").unwrap(), checksum);
+
+    let repo2 = ostree::Repo::open_at_dir(&test_repo.dir, ".").unwrap();
+    let refs = repo2
+        .list_refs(None, NONE_CANCELLABLE)
+        .expect("failed to list refs");
+    assert_eq!(1, refs.len());
+    assert_eq!(checksum, refs["test"]);
+}
+
 #[test]
 fn repo_traverse_and_read() {
     let test_repo = TestRepo::new();
index a51c0521c6ae64807a11cb6d934b4494b5024e1c..2bc4efbf78ee79e5aa1e4ff88b9684ab9794394d 100644 (file)
@@ -28,6 +28,26 @@ impl TestRepo {
     }
 }
 
+#[derive(Debug)]
+#[cfg(feature = "cap-std-apis")]
+pub struct CapTestRepo {
+    pub dir: cap_tempfile::TempDir,
+    pub repo: ostree::Repo,
+}
+
+#[cfg(feature = "cap-std-apis")]
+impl CapTestRepo {
+    pub fn new() -> Self {
+        Self::new_with_mode(ostree::RepoMode::Archive)
+    }
+
+    pub fn new_with_mode(repo_mode: ostree::RepoMode) -> Self {
+        let dir = cap_tempfile::tempdir(cap_std::ambient_authority()).unwrap();
+        let repo = ostree::Repo::create_at_dir(&dir, ".", repo_mode, None).expect("repo create");
+        Self { dir, repo }
+    }
+}
+
 pub fn create_mtree(repo: &ostree::Repo) -> ostree::MutableTree {
     let mtree = ostree::MutableTree::new();
     let file = gio::File::for_path(